<?php

namespace App\GlobalData;

use Workerman\Worker;

class Server
{

    protected $_worker = null;

    protected $_dataArray = array();

    // 客服列表
    public static $_servers = array();

    // 待接入会话列表 公共 所有客服可见
    public static $_waiting = array();

    // 已接入会话列表
    public static $_opened = array();

    // 禁言的会话列表
    public static $_banned = array();

    // 会话列表
    public static $_dialogs = array();

    // 变量锁列表
    public static $_keys = array();

    public function __construct($ip = '127.0.0.1', $port = 2207)
    {
        $worker = new Worker("frame://$ip:$port");
        $worker->count = 1;
        $worker->name = 'globalDataServer';
        $worker->onMessage = array($this, 'onMessage');
        $worker->onWorkerStop = array($this, 'onWorkerStop');
        $worker->onWorkerStart = array($this, 'onWorkerStart');
        $worker->reloadable = false;
    }

    public function onWorkerStart()
    {
        echo 'APP_ENV配置：：：：：：' . config('APP_ENV') . "\n";
        if (config('APP_ENV') != 'local' && is_file(__DIR__ . "/persistent.server")) {
            // static::$_servers = unserialize(file_get_contents(__DIR__ . "/persistent.server"));
            static::$_waiting = unserialize(file_get_contents(__DIR__ . "/persistent.waiting"));
            static::$_opened = unserialize(file_get_contents(__DIR__ . "/persistent.open"));
            static::$_banned = unserialize(file_get_contents(__DIR__ . "/persistent.ban"));
            static::$_dialogs = unserialize(file_get_contents(__DIR__ . "/persistent.dialog"));
        }
    }

    public function onWorkerStop()
    {
        if (config('APP_ENV') != 'local') {
            // file_put_contents(__DIR__ . "/persistent.server", serialize(static::$_servers));
            file_put_contents(__DIR__ . "/persistent.waiting", serialize(static::$_waiting));
            file_put_contents(__DIR__ . "/persistent.open", serialize(static::$_opened));
            file_put_contents(__DIR__ . "/persistent.ban", serialize(static::$_banned));
            file_put_contents(__DIR__ . "/persistent.dialog", serialize(static::$_dialogs));
        }
    }

    public function onMessage($connection, $buffer)
    {
        if ($buffer === 'ping') {
            return;
        }
        $data = unserialize($buffer);
        if (!$buffer || !isset($data['cmd']) || !isset($data['key'])) {
            return $connection->close(serialize('bad request'));
        }
        $cmd = $data['cmd'];
        $key = isset($data['key']) ? $data['key'] : '';
        switch ($cmd) {
            case 'getServer':
                if (isset(static::$_servers['server_id_' . $key])) {
                    return $connection->send(serialize(static::$_servers['server_id_' . $key]));
                }
                return $connection->send('b:0;');
            case 'setServer':
                static::$_servers['server_id_' . $key] = $data['value'];
                return $connection->send('b:1;');
            case 'setServerStatus':
                static::$_servers['server_id_' . $key]['status'] = $data['value']['status'];
                static::$_servers['server_id_' . $key]['rest_start'] = $data['value']['rest_start'];
                return $connection->send('b:1;');
            case 'setServerLastUser':
                if (isset(static::$_servers['server_id_' . $key])) {
                    static::$_servers['server_id_' . $key]['last_user_id'] = $data['value'];
                }
                return $connection->send('b:1;');
            case 'getOnlineServer':
                $onlineServer = [];
                foreach (static::$_servers as $server) {
                    if ($server['status'] == 'online') {
                        $onlineServer[] = $server;
                    }
                }
                return $connection->send(serialize($onlineServer));
            case 'updateDialog':
                echo '新建消息' . "\n";
                $dialog_key = 'user_id_' . $key;
                $dialog = isset(static::$_dialogs[$dialog_key]) ? static::$_dialogs[$dialog_key] : null;
                if (!is_null($dialog)) {
                    $time_now = time();
                    $dialog['unread']++;
                    $dialog['expire_time'] = $time_now + intval(config('DIALOG_EXPIRES'));
                    $content = unserialize($dialog['content']);
                    $content[] = ['role' => 'user', 'content' => $data['value']['content'], 'time' => $time_now, 'is_auto' => 0];
                    $dialog['content'] = serialize($content);
                    static::$_dialogs[$dialog_key] = $dialog;
                    // return $connection->send(serialize(static::$_dialogs[$dialog_key]));
                    return $connection->send('b:1;');
                }
                return $connection->send('b:0;');
            case 'setServerMessage':
                $dialog_key = 'user_id_' . $key;
                $dialog = isset(static::$_dialogs[$dialog_key]) ? (static::$_dialogs[$dialog_key]) : null;
                if (!is_null($dialog)) {
                    $time_now = time();
                    $content = unserialize($dialog['content']);
                    $content[] = ['role' => 'server', 'content' => $data['value']['content'], 'time' => $time_now, 'is_auto' => $data['value']['is_auto']];
                    $dialog['content'] = serialize($content);
                    static::$_dialogs[$dialog_key] = $dialog;
                    return $connection->send(serialize(static::$_dialogs[$dialog_key]));
                    return $connection->send('b:1;');
                }
                return $connection->send('b:1;');
            case 'getMemoryUsage':
                echo '使用内存' . static::getNiceFileSize(memory_get_usage()) . "\n";
                return $connection->send('b:0;');
            case 'getWaitingDialogs':
                $user_ids = array_keys(static::$_waiting);
                $data = [];
                foreach ($user_ids as $user_id) {
                    $dialog = static::$_dialogs[$user_id];
                    $data[$dialog['wechat_id']]['wechat_id'] = $dialog['wechat_id'];
                    $data[$dialog['wechat_id']]['wechat_name'] = $dialog['wechat_name'];
                    $data[$dialog['wechat_id']]['list'][] = $dialog;
                }
                if(count($data) == 0) {
                    return $connection->send(serialize([]));
                }
                ksort($data, SORT_NUMERIC);
                return $connection->send(serialize(array_values($data)));
                break;
            case 'getJoined':
                $user_ids = isset(static::$_opened['server_id_' . $key]) ? static::$_opened['server_id_' . $key] : [];
                $data = [];
                //                print_r($user_ids);
                //                print_r(static::$_dialogs);
                foreach ($user_ids as $user_id) {
                    if(!array_key_exists('user_id_' . $user_id, static::$_dialogs))
                    {
                        $index = array_search($user_id, static::$_opened['server_id_' . $key]);
                        if ($index !== false) {
                            unset(static::$_opened['server_id_' . $key][$index]);
                        }
                    }
                    $data[] = static::$_dialogs['user_id_' . $user_id];
                }
                return $connection->send(serialize($data));
                break;
            case 'hasDialog':
                return $connection->send(serialize(isset(static::$_dialogs['user_id_' . $key])));
                break;
            case 'getDialogByUserId':
                if (isset(static::$_dialogs['user_id_' . $key])) {
                    $dialog = static::$_dialogs['user_id_' . $key];
                    $dialog['content'] = unserialize($dialog['content']);
                    return $connection->send(serialize($dialog));
                }
                $connection->send('b:0;');
                break;
            case 'getDialogAndResetUnread':
                static::$_dialogs['user_id_' . $key]['unread'] = 0;
                $dialog = static::$_dialogs['user_id_' . $key];
                $dialog['content'] = unserialize($dialog['content']);
                return $connection->send(serialize($dialog));
                break;
            case 'setUnread':
                static::$_dialogs['user_id_' . $key]['unread'] = 0;
                $connection->send('b:1;');
                break;
            case 'isUserBanned':
                if (isset(static::$_banned['user_id_' . $key]) && static::$_banned['user_id_' . $key] < time()) {
                    if(isset(static::$_dialogs['user_id_' . $key])){
                        if (static::$_dialogs['user_id_' . $key]['server_id'] == 0) {
                            static::$_dialogs['user_id_' . $key]['status'] = 'pending';
                            static::$_waiting['user_id_' . $key] = $key;
                        }
                        static::$_dialogs['user_id_' . $key]['banned_at'] = null;
                    }
                    unset(static::$_banned['user_id_' . $key]);
                    return $connection->send('b:0;');
                }
                return $connection->send(serialize(isset(static::$_banned['user_id_' . $key])));
                break;
            case 'dialogExists':
                return $connection->send(serialize(isset(static::$_dialogs['user_id_' . $key])));
                break;
            case 'setDialog':
                // 新建会话 加入会话列表，设置待接入列表
                echo '新建待接入' . "\n";
                $dialog = $data['value'];
                $dialog['content'] = serialize($dialog['content']);
                static::$_dialogs['user_id_' . $key] = $dialog;
                if($dialog['status'] == 'pending'){
                    static::$_waiting['user_id_' . $key] = $key;
                }
                $connection->send('b:1;');
                break;
            case 'setWaitingDialog':
                static::$_waiting[$key] = $data['value'];
                $connection->send('b:1;');
                break;
            case 'setBannedDialog':
                // 将用户会话从待接入或已接入列表移除
                // $dialog = $data['value'];
                // print_r('小黑屋...' . $key);
                // $dialog_key = 'user_id_' . $key;
                // if ($dialog['status'] == 'pending') {
                //     unset(static::$_waiting[$dialog_key]);
                // }
                // if ($dialog['status'] == 'joined') {

                //     $index = array_search($dialog['user_id'], static::$_opened['server_id_' . $dialog['server_id']]);
                //     if ($index !== false) {
                //         unset(static::$_opened['server_id_' . $dialog['server_id']][$index]);
                //     }
                // }
                // print_r($dialog);
                // 修改会话状态为禁言，并加入禁言列表
                // $dialog['status'] = 'banned';
                // $dialog['banned_at'] = time() + config('BANNED_EXPIRES');
                static::$_dialogs['user_id_' . $key]['banned_at'] = $data['value'];
                static::$_banned['user_id_' . $key] = $data['value'];
                $connection->send('b:1;');
                break;
            case 'setAddOpened':
                // 单条接入会话
                $dialog_key = 'user_id_' . $key;
                unset(static::$_waiting[$dialog_key]);
                static::$_dialogs[$dialog_key]['status'] = 'joined';
                static::$_dialogs[$dialog_key]['server_id'] = $data['value'];
                // 加入已接入列表   $_opened[server_id][user_id]
                static::$_opened['server_id_' . $data['value']][] = $key;
                return $connection->send('b:1;');
                break;
            case 'setAddOpenedForWechat':
                // 接入公众号全部会话
                $dialogs = [];
                foreach (static::$_dialogs as $dialog) {
                    if ($dialog['status'] == 'pending' && $dialog['wechat_id'] == $key) {
                        $prefix = 'user_id_' . $dialog['user_id'];
                        unset(static::$_waiting[$prefix]);
                        // 更新客服id
                        static::$_dialogs[$prefix]['status'] = 'joined';
                        static::$_dialogs[$prefix]['server_id'] = $data['value'];
                        // 加入已接入列表   $_opened[server_id][user_id]
                        static::$_opened['server_id_' . $data['value']][] = $dialog['user_id'];
                        $dialogs[] = static::$_dialogs[$prefix]['dialog_id'];
                    }
                }
                return $connection->send(serialize($dialogs));
                break;
            case 'close':
                $dialog = static::$_dialogs['user_id_' . $key];
                static::unsetList($dialog);
                $connection->send('b:1;');
                break;
            case 'checkExpire':
                $dialog_ids = [];
                foreach (static::$_dialogs as $dialog) {
                    // print_r($dialog);
                    if ($dialog['expire_time'] < time()) {
                        print_r($dialog['user_id'] . ':::过期时间：：：' . date('Y-m-d H:i:s', $dialog['expire_time']));
                        print_r('删除会话：' . $dialog['user_id']);
                        $prefix = 'user_id_' . $dialog['user_id'];
                        $dialog_ids[] = static::$_dialogs[$prefix]['dialog_id'];
                        static::unsetList($dialog);
                    }
                }
                if (count($dialog_ids)) {
                    $connection->send(serialize($dialog_ids));
                    return;
                }
                $connection->send('b:1;');
                break;
            case 'get':
                if (!isset($this->_dataArray[$key])) {
                    return $connection->send('N;');
                }
                return $connection->send(serialize($this->_dataArray[$key]));
                break;
            case 'set':
                $this->_dataArray[$key] = $data['value'];
                $connection->send('b:1;');
                break;
            case 'add':
                if (isset($this->_dataArray[$key])) {
                    return $connection->send('b:0;');
                }
                $this->_dataArray[$key] = $data['value'];
                return $connection->send('b:1;');
                break;
            case 'increment':
                if (!isset($this->_dataArray[$key])) {
                    return $connection->send('b:0;');
                }
                if (!is_numeric($this->_dataArray[$key])) {
                    $this->_dataArray[$key] = 0;
                }
                $this->_dataArray[$key] = $this->_dataArray[$key] + $data['step'];
                return $connection->send(serialize($this->_dataArray[$key]));
                break;
            case 'cas':
                $old_value = !isset($this->_dataArray[$key]) ? null : $this->_dataArray[$key];
                if (md5(serialize($old_value)) === $data['md5']) {
                    $this->_dataArray[$key] = $data['value'];
                    return $connection->send('b:1;');
                }
                $connection->send('b:0;');
                break;
            case 'delete':
                unset($this->_dataArray[$key]);
                $connection->send('b:1;');
                break;
            default:
                $connection->close(serialize('bad cmd ' . $cmd));
        }
    }

    public static function getNiceFileSize($bytes, $binaryPrefix = true)
    {
        if ($binaryPrefix) {
            $unit = array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB');
            if ($bytes == 0) return '0 ' . $unit[0];
            return @round($bytes / pow(1024, ($i = floor(log($bytes, 1024)))), 2) . ' ' . (isset($unit[$i]) ? $unit[$i] : 'B');
        } else {
            $unit = array('B', 'KB', 'MB', 'GB', 'TB', 'PB');
            if ($bytes == 0) return '0 ' . $unit[0];
            return @round($bytes / pow(1000, ($i = floor(log($bytes, 1000)))), 2) . ' ' . (isset($unit[$i]) ? $unit[$i] : 'B');
        }
    }

    protected static function unsetList($dialog)
    {
        //        print_r($dialog);
        unset(static::$_dialogs['user_id_' . $dialog['user_id']]);
        switch ($dialog['status']) {
            case 'pending':
                unset(static::$_waiting['user_id_' . $dialog['user_id']]);
                break;
            case 'joined':
                $index = array_search($dialog['user_id'], static::$_opened['server_id_' . $dialog['server_id']]);
                //                print_r('join:' . $index);
                if ($index !== false) {
                    unset(static::$_opened['server_id_' . $dialog['server_id']][$index]);
                }
                break;
            case 'banned':
                if(static::$_banned['user_id_' . $dialog['user_id']] < time()) {
                    unset(static::$_banned['user_id_' . $dialog['user_id']]);
                }
                $index = array_search($dialog['user_id'], static::$_opened['server_id_' . $dialog['server_id']]);
                if ($index !== false) {
                    unset(static::$_opened['server_id_' . $dialog['server_id']][$index]);
                }
                break;
        }
    }
}
